/* ********************************************************** */
/*!	
	\image html AVR_Toolbox.gif
	
	<center>Visit us at: http://www.smileymicros.com </center>
	
	\mainpage SPI (Serial Peripheral Interface)
	
	
	This code was designed to allow the user to create
	multiple SPI links using either bit-banged software or regular
	AVR hardware SPI.

	The user first selects a SPI number from a list that can
	be expanded as needed:
	#define SPI0
	//#define SPI1
	//#define SPIx // place holder

	After selecting and SPI number, then the user selects either
	software or hardware for that number:
	//#define SPI0_SOFT
	#define SPI0_HARD

	The user accesses the following functions:\n
	void spi0_init_master(void);\n
	uint8_t spi0_master_rw8(uint8_t to_slave);\n
	uint16_t spi0_master_rw16(uint16_t to_slave);\n

	Which are alia's for the software or hardware version.

	This code was tested for SPI0 in both software and hardware
	modes on the ATmega169, ATmega328, and ATmega644 (TODO)

	\todo 1. Test it for the ATmega644.
	\todo 2. Retest with the Arduino board.
	\todo 3. Improve the comments before letting this puppy loose!

	\author Joe Pardue 
	\date October 29, 2010
*/





/*****************************************************
	SPI.c Joe Pardue October 29, 2010
	
	>>WORK IN PROGRESS<<
	Soft SPI beta September 10, 2010 tested with CylonEyesSPI on Butterfly
	Soft and Hard tested on Arduino board September 18, 2010

	TODO: Setup and test an ATmega644 version

******************************************************/

// I HATE LICENSES LIKE THIS >>BUT<< I've been told that without
// the license then the work is automatically copyrighted in my name
// since my purpose is to educate (and learn), I want the code to be 
// used by whoever wants to use it to learn something. If you like it, 
// then visit my website www.smileymicros.com and buy something.

/*
 *  BSD License
 *  -----------
 *
 *  Copyright (c) 2008, Smiley Micros, All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. 
 *   
 *  - Neither the name of the Smiley Micros nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission. 
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

// And to further cover my butt, let me add that if you use this software
// it will destroy whatever machine you use it on and kill anyone in a one 
// kilometer radius. So don't even consider using it for any reason whatsoever!


/*****************************************************
 	SPI.c provides a library of SPI functions for 
	two types of SPI:

		Software SPI
		Hardware SPI

******************************************************/

#include "SPI.h"

#if defined (SPI0)

/*****************************************************
	Define SPI0 functions
******************************************************/


/* ********************************************************** */
/*!	
	\brief Initializes spi0 as master.

	\note Selects hardware or software SPI based on constant
	 defintion SPI0_SOFT.

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
void spi0_init_master(void)
{
	#if defined (SPI0_SOFT)
	spi0_soft_init_master();
	#else // HARD
	spi0_hard_init_master();
	#endif
}

/* ********************************************************** */
/*!	
	\brief Writes and reads an 8-bit byte via SPI.

	\note Selects hardware or software SPI based on constant
	 defintion SPI0_SOFT.

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
uint8_t spi0_master_rw8(uint8_t to_slave)
{
	#if defined (SPI0_SOFT)
	return spi0_soft_master_rw8(to_slave);
	#else // HARD
	return spi0_hard_master_rw8(to_slave);
	#endif
}


/* ********************************************************** */
/*!	
	\brief Writes and reads a 16-bit word via SPI.

	\note Selects hardware or software SPI based on constant
	 defintion SPI0_SOFT.

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
uint16_t spi0_master_rw16(uint16_t to_slave)
{
	#if defined (SPI0_SOFT)
	return spi0_soft_master_rw16(to_slave);
	#else // HARD
	return spi0_hard_master_rw16(to_slave);
	#endif
}

#elif defined (SPI1)
/*****************************************************
	Define SPI1 functions
******************************************************/

#elif defined (SPIn)
/*****************************************************
	Define SPIn functions
******************************************************/

#else
   #error "No SPI Number Defined"
#endif


#if defined (SPI0_SOFT)
/*****************************************************
	Software SPI
******************************************************/
/* ********************************************************** */
/*!	
	\brief Initializes a software SPI for spi0.

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
void spi0_soft_init_master()
{
  	// Initialize MISO as input
  	// set DDR pin register to 0
  	miso_ddr &= ~(1<<miso_port_pin);

  	// Initialize MOSI, SCLK, AND /SS as outputs
  	// set DDR pin registers to 1
    mosi_ddr |= (1<<mosi_port_pin);
	sclk_ddr |= (1<<sclk_port_pin);
  	ss_ddr |= (1<<ss_port_pin); 
}

/* ********************************************************** */
/*!	
	\brief Writes and reads an 8-bit byte via software SPI
	for spi0.

	\note Selects hardware or software SPI based on constant
	 defintion SPI0_SOFT

	\return 8-bit byte from the slave device.

	\param to_slave - 8-bit byte to send to the slave device.

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
uint8_t spi0_soft_master_rw8(uint8_t to_slave)
{
	uint8_t from_slave = 0;

	spi_soft_set_ss();

	for(int i=0; i<=7; i++)
	{
		// Send a bit
		if(is_bit_set(to_slave,i))
			spi_soft_set_mosi_bit();
		else
			spi_soft_clear_mosi_bit();

		// Receive a bit
		// temp bits start 0 so change only if 1
		if (miso_port_pins & (1<<miso_port_pin))
			from_slave |= (1 << i);

   		// Toggle the clock
    	spi_soft_toggle_sclk();
	}

	spi_soft_clear_ss();

	return from_slave;

}

/* ********************************************************** */
/*!	
	\brief Writes and reads a 16-bit word via software SPI
	for spi0.

	\note Selects hardware or software SPI based on constant
	 defintion SPI0_SOFT.

	\return 16-bit word from the slave device.

	\param to_slave - 16-bit word to send to the slave device.

	\author October 29, 2010 
	\date 9/22/10
	\version 1.00
*/
/* ********************************************************** */
uint16_t spi0_soft_master_rw16(uint16_t to_slave)
{
	uint16_t from_slave = 0;

	// Select the slave clear /SS to 0
	spi_soft_clear_ss();

	for(int i=0; i<=15; i++)
	{
		// Send a bit
		if(is_bit_set(to_slave,i))
			spi_soft_set_mosi_bit();
		else
			spi_soft_clear_mosi_bit();

		// Receive a bit
		// temp bits start 0 so change only if 1
		if (miso_port_pins & (1<<miso_port_pin))
			from_slave |= (1 << i);

   		// Toggle the clock
    	spi_soft_toggle_sclk();
	}

	// Deselect the slave set /SS to 1
	spi_soft_set_ss();
	
	return from_slave;
}


/* ********************************************************** */
/*!	
	\brief Return the data pin state

	\note Checks MISO bit state. 

	\return 1 if bit set, otherwise zero

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
uint8_t spi_soft_get_miso_bit()
{
   if (miso_port_pins & (1<<miso_port_pin))
   {
     return 1;
   }
   else return 0;
}


/* ********************************************************** */
/*!	
	\brief Toggles the SCLK line

	\note Hardwired _delay_ms(0.2) is a swag. 

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
// Toggle the clock
void spi_soft_toggle_sclk()
{
	// Serial bit shifted in on rising clock
   sclk_port |= (1<<sclk_port_pin); 

   // Give some time to react
   _delay_ms(0.2);

   // Set the clock back to 0
   sclk_port &= ~(1<<sclk_port_pin);
}

#elif defined (SPI0_HARD)
/*****************************************************
	Hardware SPI
******************************************************/

/* ********************************************************** */
/*!	
	\brief Initializes the AVR hardware SPI for spi0

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
void spi0_hard_init_master(void)
{
	PORTB |= (1<< miso_hardware_pin) \
 			| (1<< mosi_hardware_pin) \
 			| (1<< sclk_hardware_pin) \
 			| (1<< ss_hardware_pin);

	//Set MOSI, SCK AND SS to outputs
	DDRB |= (1<< mosi_hardware_ddr) \
			| (1<< sclk_hardware_ddr) \
			| (1<< ss_hardware_ddr);

	// Set Miso to input
	DDRB &= ~(1<<miso_hardware_ddr);

    SPCR = ( 1 << SPE ) | ( 1 << MSTR ) | ( 1 << SPR0 ); 
}

/* ********************************************************** */
/*!	
	\brief Writes and reads an 8-bit byte via hardware SPI
	for spi0.

	\note Blocks waiting for send to complete

	\return 8-bit byte from the slave device.

	\param to_slave - 8-bit byte to send to the slave device.

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
uint8_t spi0_hard_master_rw8(uint8_t to_slave)
{
	uint8_t from_slave;

	// select slave
	spi_hard_set_ss();

	// Put byte in SPI data register
	SPDR = to_slave;	

	// Poll SPIF-flag until transfer complete				
	while(!(SPSR & (1<<SPIF)));					

	// Get the SPI data reg.
	from_slave = SPDR;	

	// deselect slave
	spi_hard_clear_ss();
							
	//return it
	return from_slave;
							
}

/* ********************************************************** */
/*!	
	\brief Writes and reads a 16-bit word via hardware SPI
	for spi0.

	\note Blocks waiting for send to complete

	\return 16-bit word from the slave device.

	\param to_slave - 16-bit word to send to the slave device.

	\author Joe Pardue 
	\date October 29, 2010
	\version 1.00
*/
/* ********************************************************** */
uint16_t spi0_hard_master_rw16(uint16_t to_slave)
{
	uint8_t to_slave_low = (uint8_t) to_slave;
	uint8_t to_slave_high = (uint8_t) (to_slave >> 8);
	uint8_t from_slave_high = 0;
	uint8_t from_slave_low = 0;

	// select slave
	spi_hard_set_ss();

	// Put byte in SPI data register
	SPDR = to_slave_high;	

	// Poll SPIF-flag until transfer complete				
	while(!(SPSR & (1<<SPIF)));

	// Get the SPI data reg.
	from_slave_low = SPDR;

	// Put byte in SPI data register
	SPDR = to_slave_low;	

	// Poll SPIF-flag until transfer complete				
	while(!(SPSR & (1<<SPIF)));

	// Get the SPI data reg.
	from_slave_high = SPDR;
		
	// deselect slave
	spi_hard_clear_ss();

	return ( (uint16_t)(from_slave_high << 8) + (uint16_t)from_slave_low );						
}

#else
   #error "No SPI Type Defined"
#endif
